package edu.pitt.dbmi.odie.gapp.gwt.server.util.gdata;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.apache.commons.lang.CharUtils;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gdata.client.Service.GDataRequest;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.Link;
import com.google.gdata.data.docs.DocumentListEntry;
import com.google.gdata.data.docs.DocumentListFeed;
import com.google.gdata.data.extensions.LastModifiedBy;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;
import com.google.gdata.util.common.xml.XmlWriter;

import edu.pitt.dbmi.odie.gapp.gwt.server.user.ODIE_TruncateDatabaseServlet;
import edu.pitt.dbmi.odie.gapp.gwt.server.user.ODIE_ServerSideLoginInfo;
import edu.pitt.dbmi.odie.gapp.gwt.server.util.rest.ODIE_NcboRestUtils;
import edu.upmc.opi.caBIG.caTIES.common.CaTIES_JDomUtils;

public class ODIE_GDataDocsService {
	
	private static final Logger log = Logger
	.getLogger(ODIE_GDataDocsService.class.getName());
	
	public static final String DEFAULT_HOST = "docs.google.com";
	public static final String URL_FEED = "/feeds";
	public static final String URL_DOWNLOAD = "/download";
	public static final String URL_CATEGORY_EXPORT = "/Export";
	public static final String UMLS_LICENSE_NAME = "UMLS_LICENSE";
	public static final String URL_GROUP_FOLDERS = "/folders";
	public static final String URL_PATH = "/private/full";
	private static final String URL_DOCUMENTS_LIST = "/feeds/documents/private/full/-/document?prettyprint=true";

	private DocsService docsService = null;
	
	private ODIE_ServerSideLoginInfo serverSideLoginInfo = null ;

	public ODIE_GDataDocsService(ODIE_ServerSideLoginInfo serverSideLoginInfo) {
		try {
			setServerSideLoginInfo(serverSideLoginInfo) ;
			docsService = new DocsService("webodie");
			log.info("Instantiated new DocsService called webodie") ;
			if (!serverSideLoginInfo.getAuthSubSessionToken().equals("UNDEFINED")) {
				docsService.setAuthSubToken(serverSideLoginInfo
						.getAuthSubSessionToken(), null /* private key */);
				log.info("Authenticated with the docsService using authSubSessionToken " + serverSideLoginInfo
						.getAuthSubSessionToken()) ;
			} else if (!serverSideLoginInfo.getAuthSubToken().equals("UNDEFINED")) {
				docsService.setAuthSubToken(serverSideLoginInfo
						.getAuthSubToken(), null /* private key */);
				log.info("Authenticated with the docsService using authSubToken " + serverSideLoginInfo
						.getAuthSubToken()) ;
			} else if (ODIE_NcboRestUtils.isPosLen(serverSideLoginInfo
					.getEmailAddress())
					&& ODIE_NcboRestUtils.isPosLen(serverSideLoginInfo
							.getPassword())) {
				docsService.setUserCredentials(serverSideLoginInfo
						.getEmailAddress(), serverSideLoginInfo.getPassword());
				log.info("Authenticated with the docsService using email and password " + serverSideLoginInfo
						.getEmailAddress()) ;
			} else {
				docsService = null;
			}
			
//			log.info("Service directory test ==> " + testDocsService(docsService)) ;

		} catch (AuthenticationException e) {
			log.info("ODIE_GDataDocsService failed with message " + e.getMessage()) ;
		} catch (Exception e) {
			log.info("ODIE_GDataDocsService failed with message " + e.getMessage()) ;
		}
	}
	
	private boolean testDocsService(DocsService docsService) {
		boolean result = false;
		try {
			DocumentListFeed feed;
			URL documentListFeedUrl = new URL("http://docs.google.com/feeds/default/private/full");
//			documentListFeedUrl = new URL(
//					"http://docs.google.com/feeds/default/private/full");
			// These System Properties turn off a warning message from within
			// the GData documents service
			System.setProperty("org.apache.commons.logging.Log",
					"org.apache.commons.logging.impl.SimpleLog");
			System
					.setProperty(
							"org.apache.commons.logging.simplelog.showdatetime",
							"true");
			System
					.setProperty(
							"org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient",
							"error");
			feed = docsService.getFeed(documentListFeedUrl,
					DocumentListFeed.class);
			List<DocumentListEntry> entries = feed.getEntries();
			if (entries != null && !entries.isEmpty()) {
				result = true;
				DocumentListEntry entry = entries.get(0) ;
				log.info(printDocumentEntry(entry)) ;
			}

		} catch (AuthenticationException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public String printDocumentEntry(DocumentListEntry doc) {
		StringBuffer sb = new StringBuffer() ;
		String resourceId = doc.getResourceId();
		String docType = resourceId.substring(0, resourceId.lastIndexOf(':'));
		
		sb.append("'" + doc.getTitle().getPlainText() + "' ("
				+ docType + ")");
		sb.append("  link to Google Docs: "
				+ doc.getHtmlLink().getHref());
		sb.append("  resource id: " + resourceId);

		// print the timestamp the document was last viewed
		DateTime lastViewed = doc.getLastViewed();
		if (lastViewed != null) {
			sb.append("  last viewed: " + lastViewed.toString());
		}

		// print who made that modification
		LastModifiedBy lastModifiedBy = doc.getLastModifiedBy();
		if (lastModifiedBy != null) {
			sb.append("  updated by: " + lastModifiedBy.getName()
					+ " - " + lastModifiedBy.getEmail());
		}

		// print other useful metadata
		sb.append("  last updated: " + doc.getUpdated().toString());
		sb.append("  viewed by user? " + doc.isViewed());
		sb.append("  writersCanInvite? "
				+ doc.isWritersCanInvite().toString());
		sb.append("  hidden? " + doc.isHidden());
		sb.append("  starred? " + doc.isStarred());
		return sb.toString() ;
	}
	
	public ODIE_ServerSideLoginInfo getServerSideLoginInfo() {
		return serverSideLoginInfo;
	}

	public void setServerSideLoginInfo(ODIE_ServerSideLoginInfo serverSideLoginInfo) {
		this.serverSideLoginInfo = serverSideLoginInfo;
	}

	public DocsService getDocsService() {
		return docsService;
	}

	public void setDocsService(DocsService docsService) {
		this.docsService = docsService;
	}

	@SuppressWarnings("deprecation")
	public String pullDocumentContent(String simpleDocId) {

		String result = null;
		InputStream inStream = null;
		
		int attemptUpperBound = 3 ;
		int currentAttempt = 0 ;
		boolean isAttempting = true ;
		while (isAttempting) {
			try {
				String docType = "document";
				String format = "txt";
				String[] parameters = { "docID=" + simpleDocId,
						"exportFormat=" + format };
				URL url = buildUrl(URL_DOWNLOAD + "/" + docType + "s"
						+ URL_CATEGORY_EXPORT, parameters);

				Link link = new Link();
				link.setHref(url.toString());

				inStream = this.docsService.getStreamFromLink(link);
				String nonAsciiResult = ODIE_NcboRestUtils.getString(inStream,
						ODIE_NcboRestUtils.CONST_CHAR_ENCODING_ISO);

				StringBuffer sb = new StringBuffer();
				char[] nonAsciiCharArray = nonAsciiResult.toCharArray();
				for (int idx = 0; idx < nonAsciiCharArray.length; idx++) {
					char nonAsciiChar = nonAsciiCharArray[idx];
					if (CharUtils.isAscii(nonAsciiChar) && nonAsciiChar != '\r') {
						sb.append(nonAsciiCharArray[idx]);
					}
				}
				result = sb.toString();
				
				isAttempting = false ;

			} catch (MalformedURLException e) {
				;
			} catch (DocumentListException e) {
				;
			} catch (IOException e) {
				;
			} catch (ServiceException e) {
				;
			}
		}
		
		if (isAttempting) {
			Log.warn("Failed to read document with simpleDocId = " + simpleDocId) ;
		}
		

		return result;
	}

	public String pullDocumentContent2(String simpleDocId) {

		String result = null;
		try {
			String docType = "document";
			String format = "txt";
			String[] parameters = { "docID=" + simpleDocId,
					"exportFormat=" + format };
			URL url = buildUrl(URL_DOWNLOAD + "/" + docType + "s"
					+ URL_CATEGORY_EXPORT, parameters);

			Log.debug("Pulling document with url ==> \n"
					+ url.toExternalForm());

			Link link = new Link();
			link.setHref(url.toString());

			GDataRequest request = this.docsService
					.createLinkQueryRequest(link);
			request.execute();
			String nonAsciiResult = parseResponse(request);
			
			Log.debug(nonAsciiResult) ;

			StringBuffer sb = new StringBuffer();
			char[] nonAsciiCharArray = nonAsciiResult.toCharArray();
			for (int idx = 0; idx < nonAsciiCharArray.length; idx++) {
				char nonAsciiChar = nonAsciiCharArray[idx];
				if (CharUtils.isAscii(nonAsciiChar) || nonAsciiChar == '\n') {
					sb.append(nonAsciiCharArray[idx]);
				}
				else if (nonAsciiChar == '\r') {
					sb.append('\n') ;
				}
			}
			result = sb.toString();
			
			Log.debug(result) ;

			request.end();

		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (DocumentListException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ServiceException e) {
			e.printStackTrace();
		}

		return result;
	}

	public String pullUmlsLicenseContents() {

		String licenseCode = null;

		try {
			String urlName = "http://" + ODIE_GDataDocsService.DEFAULT_HOST
					+ ODIE_GDataDocsService.URL_DOCUMENTS_LIST;
			URL documentListFeedUrl = new URL(urlName);
			DocumentListFeed feed = docsService.getFeed(documentListFeedUrl,
					DocumentListFeed.class);
			for (DocumentListEntry doc : feed.getEntries()) {
				String title = doc.getTitle().getPlainText();
				if (title.equalsIgnoreCase(UMLS_LICENSE_NAME)) {
					String shortId = doc.getId().substring(
							doc.getId().lastIndexOf('/') + 1);
					licenseCode = pullDocumentContent(shortId);
					if (!ODIE_NcboRestUtils.isPosLen(licenseCode)) {
						Log.warn("Failed to get license code text.");
					} else {
						Log.debug(" Got UMLS_LICENSE -- Document("
								+ shortId + "/" + doc.getTitle().getPlainText()
								+ ")" + licenseCode);
					}
					break;
				}
			}
		} catch (MalformedURLException e) {
			licenseCode = null;
		} catch (IOException e) {
			licenseCode = null;
		} catch (ServiceException e) {
			licenseCode = null;
		}

		return licenseCode;
	}

	public String pullDocumentContentByUrlFAILS(String simpleDocId) {
		String result = null;
		String documentUri = "http://docs.google.com/RawDocContents?action=fetch&justBody=true&revision=_latest&editMode=false&docID="
				+ simpleDocId;
		Log.debug("Reading raw content ==> " + documentUri);
		try {
			result = ODIE_NcboRestUtils.getPayLoad(documentUri);
		} catch (Exception x) {
			x.printStackTrace();
		}
		return result;
	}

	public String pullDocumentContentWithRawDocContents(String simpleDocId) {
		String result = null;
		String documentUri = "http://docs.google.com/RawDocContents?action=fetch&justBody=true&revision=_latest&editMode=false&docID="
				+ simpleDocId;
		Log.debug("Reading raw content ==> " + documentUri);
		try {
			URL url = new URL(documentUri);
			GDataRequest request = docsService.createEntryRequest(url);
			request.execute();
			result = parseResponse(request);
		} catch (Exception x) {
			x.printStackTrace();
		}
		return result;
	}

	public String parseResponse(GDataRequest request) {
		String result = null;
		try {
			InputStream is = request.getResponseStream();
			BufferedInputStream bis = new BufferedInputStream(is);
			ArrayList<Byte> accumulator = new ArrayList<Byte>();
			int bufferSize = 1024;
			byte[] bytes = new byte[bufferSize];
			int bytesRead = bis.read(bytes, 0, bufferSize);
			while (bytesRead > 0) {
				for (int bdx = 0; bdx < bufferSize; bdx++) {
					byte b = bytes[bdx];
					if (!Character.isISOControl((char) b)) {
						accumulator.add(bytes[bdx]);
					}
				}
				bytes = new byte[bufferSize];
				bytesRead = bis.read(bytes, 0, bufferSize);
			}
			bytes = new byte[accumulator.size()];
			int bdx = 0;
			for (Byte b : accumulator) {
				bytes[bdx++] = b;
			}
			result = new String(bytes);
			result = convertFromHtmlToPlainText(result);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return result;
	}

	public String convertFromPlainTextToHtml(String plainText) {
		String result = null;
		// result = URLDecoder.decode(htmlText, "UTF-8" );
		result = plainText;
		// result = result.replaceAll("&nbsp;", " ");
		result = result.replaceAll("<", "&lt;");
		result = result.replaceAll(">", "&gt;");
		result = result.replaceAll("&quot;", "\"");
		result = result.replaceAll("<br>", "\n");
		result = result.replaceAll("<br\\s*/>", "\n");
		result = result.replaceAll("^\\s*", "").replaceAll("\\s*$", "");

		return result;
	}

	public String convertFromHtmlToPlainText(String htmlText) {
		String result = null;
		// result = URLDecoder.decode(htmlText, "UTF-8" );
		result = htmlText;
		result = result.replaceAll("&nbsp;", " ");
		result = result.replaceAll("&lt;", "<");
		result = result.replaceAll("&gt;", ">");
		result = result.replaceAll("&quot;", "\"");
		result = result.replaceAll("<br>", "\n");
		result = result.replaceAll("<br\\s*/>", "\n");
		result = result.replaceAll("^\\s*", "").replaceAll("\\s*$", "");

		return result;
	}

	public String extractDocumentXml(DocumentListEntry doc) {
		String result = null;
		try {
			String docID = doc.getId();
			String docTitle = doc.getTitle().getPlainText();
			
			StringWriter sw = new StringWriter();
			XmlWriter xmlWriter = new XmlWriter(sw);
			ExtensionProfile arbitraryProfile = new ExtensionProfile();
			arbitraryProfile.setArbitraryXml(true);
			doc.generateAtom(xmlWriter, arbitraryProfile);

			String entryContent = prettyXmlFragment(sw.toString());
			result = entryContent;

		} catch (IOException e) {
			e.printStackTrace();
		}

		return result;

	}

	public String prettyXmlFragment(String fragment) {
		String result = fragment;
		try {
			SAXBuilder builder = new SAXBuilder();
			ByteArrayInputStream inputStream = new ByteArrayInputStream(
					fragment.getBytes());
			org.jdom.Document doc;
			doc = builder.build(inputStream);
			result = CaTIES_JDomUtils.convertDocumentToString(doc, Format
					.getPrettyFormat());
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	public String stripLeadingDocumentType(String documentId) {
		String result = documentId;
		int sPos = documentId.lastIndexOf("%");
		if (sPos == -1) {
			sPos = documentId.lastIndexOf(":");
		}
		if (sPos > 0) {
			result = documentId.substring(sPos + 1, documentId.length());
		}
		return result;
	}

	/**
	 * Builds a URL with parameters.
	 * 
	 * @param path
	 *            the path to add to the protocol/host
	 * @param parameters
	 *            parameters to be added to the URL.
	 * 
	 * @throws MalformedURLException
	 * @throws DocumentListException
	 */
	private URL buildUrl(String path, String[] parameters)
			throws MalformedURLException, DocumentListException {
		if (path == null) {
			throw new DocumentListException("null path");
		}

		return buildUrl(DEFAULT_HOST, path, parameters);
	}

	/**
	 * Builds a URL with parameters.
	 * 
	 * @param host
	 *            the domain of the server
	 * @param path
	 *            the path to add to the protocol/host
	 * @param parameters
	 *            parameters to be added to the URL.
	 * 
	 * @throws MalformedURLException
	 * @throws DocumentListException
	 */
	private URL buildUrl(String host, String path, String[] parameters)
			throws MalformedURLException, DocumentListException {
		if (path == null) {
			throw new DocumentListException("null path");
		}

		StringBuffer url = new StringBuffer();
		url.append("http://" + host + URL_FEED + path);

		if (parameters != null && parameters.length > 0) {
			url.append("?");
			for (int i = 0; i < parameters.length; i++) {
				url.append(parameters[i]);
				if (i != (parameters.length - 1)) {
					url.append("&");
				}
			}
		}

		return new URL(url.toString());
	}

	@SuppressWarnings("serial")
	class DocumentListException extends Exception {
		public DocumentListException() {
			super();
		}

		public DocumentListException(String msg) {
			super(msg);
		}
	}

}
